﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using System.Globalization;

namespace LockCrypt.Core {
    public class Utils {
        public static Random RandomSeed = new Random();

        /// <summary>
        /// Retrieves the date the assembly was compiled.
        /// </summary>
        /// <returns>The date the assembly was compiled.</returns>
        public static DateTime RetrieveLinkerTimestamp() {
            string filePath = System.Reflection.Assembly.GetCallingAssembly().GetName().CodeBase;
            const int c_PeHeaderOffset = 60;
            const int c_LinkerTimestampOffset = 8;
            byte[] b = new byte[2048];
            System.IO.Stream s = null;
            try {
                s = new System.IO.FileStream(filePath, System.IO.FileMode.Open, System.IO.FileAccess.Read);
                s.Read(b, 0, 2048);
            } finally {
                if(s != null) {
                    s.Close();
                }
            }
            int i = System.BitConverter.ToInt32(b, c_PeHeaderOffset);
            int secondsSince1970 = System.BitConverter.ToInt32(b, i + c_LinkerTimestampOffset);
            DateTime dt = new DateTime(1970, 1, 1, 0, 0, 0);
            dt = dt.AddSeconds(secondsSince1970);
            dt = dt.AddHours(TimeZone.CurrentTimeZone.GetUtcOffset(dt).Hours);
            return dt;
        }

        /// <summary>
        /// Gets the ISO 3166 code of the specified culture.
        /// </summary>
        /// <param name="info">The CultureInfo representing the culture.</param>
        /// <returns>An ISO3166 code to reresent a country which uses the CultureInfo specified</returns>
        public static string GetISO3166(CultureInfo info) {
            if(info.IsNeutralCulture) {
                if(info.TwoLetterISOLanguageName == "zh") { // chinese is awkward
                    return "cn";
                } else {
                    return new RegionInfo(CultureInfo.CreateSpecificCulture(info.Name).LCID).TwoLetterISORegionName;
                }
            } else {
                return new RegionInfo(info.Name).TwoLetterISORegionName;
            }
        }

        /// <summary>
        /// Reads the contents of the stream into a byte array.
        /// data is returned as a byte array. An IOException is
        /// thrown if any of the underlying IO calls fail.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <returns>
        /// A byte array containing the contents of the stream.
        /// </returns>
        /// <exception cref="NotSupportedException">The stream does not support reading.</exception>
        /// <exception cref="ObjectDisposedException">Methods were called after the stream was closed.</exception>
        /// <exception cref="System.IO.IOException">An I/O error occurs.</exception>
        public static byte[] ReadAllBytes(Stream stream, int initialLength) {
            // If we've been passed an unhelpful initial length, just use 32K.
            if(initialLength < 1) {
                initialLength = 32768;
            }

            byte[] buffer = new byte[initialLength];
            int read = 0;

            int chunk;
            while((chunk = stream.Read(buffer, read, buffer.Length - read)) > 0) {
                read += chunk;

                // If we've reached the end of our buffer, check to see if there's
                // any more information
                if(read == buffer.Length) {
                    int nextByte = stream.ReadByte();

                    // End of stream? If so, we're done
                    if(nextByte == -1) {
                        return buffer;
                    }

                    // Nope. Resize the buffer, put in the byte we've just
                    // read, and continue
                    byte[] newBuffer = new byte[buffer.Length * 2];
                    Array.Copy(buffer, newBuffer, buffer.Length);
                    newBuffer[read] = (byte)nextByte;
                    buffer = newBuffer;
                    read++;
                }
            }
            // Buffer is now too big. Shrink it.
            byte[] ret = new byte[read];
            Array.Copy(buffer, ret, read);
            return ret;
        }

        public struct SnapshotSize {
            public int Width;
            public int Height;
            public SnapshotSize(int width, int height) {
                Width = width;
                Height = height;
            }
        }
    }
}